package com.wqnmdb.im.service.minio;

import com.alibaba.fastjson.JSON;
import com.wqnmdb.im.config.MinIoProperties;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.PutObjectOptions;
import io.minio.Result;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import io.minio.messages.Upload;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * minlo工具类
 */
@Slf4j
@Service
public class MinioService {

    @Autowired
    MinIoProperties minIoProperties;

    private final static int expires = 60 * 60 * 24 *365;

    private static MinioClient minioClient;

    /**
     * 初始化minio配置
     */
    @PostConstruct
    public void init() {
        try {
            minioClient = new MinioClient(minIoProperties.getUrl(), minIoProperties.getAccessKey(),
                    minIoProperties.getSecretKey());
            createBucket(minIoProperties.getBucketName());
        } catch (Exception e) {
            e.printStackTrace();
            log.error("初始化minio配置异常: 【{}】", e.fillInStackTrace());
        }
    }

    /**
     * 判断 bucket是否存在
     * @param bucketName:桶名
     */
    @SneakyThrows(Exception.class)
    public boolean bucketExists(String bucketName) {
        return minioClient.bucketExists(bucketName);
    }

    /**
     * 创建 bucket
     * @param bucketName:桶名
     */
    @SneakyThrows(Exception.class)
    public void createBucket(String bucketName) {
        boolean isExist = minioClient.bucketExists(bucketName);
        if (!isExist) {
            minioClient.makeBucket(bucketName);
        }
    }

    /**
     * 获取全部bucket
     */
    @SneakyThrows(Exception.class)
    public List<Bucket> getAllBuckets() {
        return minioClient.listBuckets();
    }

    /**
     * 文件上传
     * @param bucketName:桶名
     * @param fileName:文件名
     * @param filePath:文件路径
     */
    @SneakyThrows(Exception.class)
    public void upload(String bucketName, String fileName, String filePath) {
        minioClient.putObject(bucketName, fileName, filePath, null);
    }

    /**
     * 文件上传
     * @param bucketName:桶名
     * @param fileName:文件名
     * @param stream:文件流
     */
    @SneakyThrows(Exception.class)
    public String upload(String bucketName, String fileName, InputStream stream) {
        minioClient.putObject(bucketName, fileName, stream, new PutObjectOptions(stream.available(), -1));
        return getFileUrl(bucketName, fileName, expires);
}

    @SneakyThrows(Exception.class)
    public String upload(String bucketName, MultipartFile multipartFile) {
        final String fileName = multipartFile.getOriginalFilename();
        final InputStream inputStream = multipartFile.getInputStream();
        // PutObjectOptions，上传配置(文件大小，内存中文件分片大小)
        PutObjectOptions putObjectOptions = new PutObjectOptions(inputStream.available(), -1);
        // 文件的ContentType
        putObjectOptions.setContentType(multipartFile.getContentType());
        minioClient.putObject(bucketName, fileName, inputStream, putObjectOptions);
        inputStream.close();
        return getFileUrl(bucketName, fileName, expires);
    }

    /**
     * 文件上传(断点续传)
     * @param bucketName :桶名
     * @param prefix :文件前缀
     * @param recursive :是否递归查找
     */
    @SneakyThrows(Exception.class)
    public Iterable<Result<Upload>> wcfUpload(String bucketName, String prefix, boolean recursive) {
        Iterable<Result<Upload>> results = minioClient.listIncompleteUploads(bucketName, prefix, recursive);
        log.info("断点文件数量：{}.", JSON.toJSONString(results));
        return results;
    }

    /**
     * 删除上传未完成的文件
     * @param bucketName:桶名
     * @param fileName:文件名
     */
    @SneakyThrows(Exception.class)
    public void removeWcf (String bucketName, String fileName) {
        minioClient.removeIncompleteUpload(bucketName, fileName);
    }


    /**
     * 删除文件
     * @param bucketName:桶名
     * @param fileName:文件名
     */
    @SneakyThrows(Exception.class)
    public static void deleteFile(String bucketName, String fileName) {
        minioClient.removeObject(bucketName, fileName);
    }

    /**
     * 下载文件
     * @param bucketName:桶名
     * @param fileName:文件名
     * @param response:
     */
    @SneakyThrows(Exception.class)
    public void download(String bucketName, String fileName, HttpServletResponse response) {
        // 获取对象的元数据
        final ObjectStat stat = minioClient.statObject(bucketName, fileName);
        response.setContentType(stat.contentType());
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        InputStream is = minioClient.getObject(bucketName, fileName);
        IOUtils.copy(is, response.getOutputStream());
        is.close();
    }

    /**
     * 获取minio文件加密过的地址可以通过连接下载文件
     * @param bucketName:桶名
     * @param fileName: 文件名
     * @param expires:有效时间
     */
    @SneakyThrows(Exception.class)
    public static String downloadByUrl(String bucketName, String fileName, Integer expires) {
        return minioClient.presignedGetObject(bucketName, fileName, expires);
    }

    /**
     * 设置有效期的分享链接
     * @param bucketName:桶名
     * @param fileName:文件名
     * @param expires:有效时间
     */
    @SneakyThrows(Exception.class)
    public String getFileUrl(String bucketName, String fileName, Integer expires) {
        return minioClient.getPresignedObjectUrl(Method.GET, bucketName, fileName, expires, null);
    }

    /**
     * @Description 列出存储桶中的所有对象名称
     */
    @SneakyThrows(Exception.class)
    public List<String> listObjectNames(String bucketName) {
        List<String> listObjectNames = new ArrayList<>();
        boolean flag = bucketExists(bucketName);
        if (flag) {
            Iterable<Result<Item>> myObjects = minioClient.listObjects(bucketName);
            for (Result<Item> result : myObjects) {
                Item item = result.get();
                listObjectNames.add(item.objectName());
            }
        }
        return listObjectNames;
    }
}